package eval;

import config.Config;
import earlyDetection.*;
import earlyDetection.pg.PGEdge;
import network.Device;
import network.Network;
import network.Port;
import network.Rule;
import networkLoader.FBNetwork;
import org.jgrapht.Graph;
import org.jgrapht.graph.AbstractBaseGraph;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;
import verifier.JiffyVerifier;
import verifier.Ports.Ports;
import verifier.util.Changes;

import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;

public class PolicyCheckEvalAllPair {
    static long ip = (111 << 24) + (1 << 16);

    public static void main(String[] args) {
        Network network = FBNetwork.getNetworkSrcHackMore();
        Config.logging = false;
        for (int i = 0; i < 0; i++) {
            allPairCheck(network);
        }
        System.gc();
        Config.logging = true;
        allPairCheck(network);
//        pgCheck(network);
//        naiveCheck(network);
//        naiveCheckIncre(network);
    }

    public static void allPairCheck(Network network) {
        ip = (2 << 16);

        JiffyVerifier verifier = new JiffyVerifier(network);

        AllPairChecker policyChecker = new AllPairChecker(network, verifier.bddEngine, verifier.bddEngine.encodeIpv4(BigInteger.valueOf(ip), 16));
        Config.startAt = System.nanoTime();

        List<Device> allDevices = new ArrayList<>(network.getAllDevices());
//        Collections.shuffle(allDevices);
        for (Device device : allDevices) {
            if (policyChecker.foundBug) return;
            List<Rule> rulesInSubspace = device.getInitialRules().stream()
                    .filter(rule -> (rule.getMatch().longValue() >> 16) == 2)
                    .collect(Collectors.toList());
            Changes changes = verifier.insertMiniBatch(rulesInSubspace);
            verifier.update(changes);
            Config.processedUpdates += device.getInitialRules().size();
//            if (policyChecker.stop) {
//                continue;
//            }
            HashMap<Integer, Ports> ecToPorts = new HashMap<>();
            for (Map.Entry<Ports, Integer> entry : verifier.portsToPredicate.entrySet()) {
                ecToPorts.put(entry.getValue(), entry.getKey());
            }
            long s = System.nanoTime();
//            policyChecker.check(verifier.portsToPredicate.values(), device, ecToPorts, verifier.getPortToPredicate());
//            System.out.println(System.nanoTime() - s);
        }
        System.out.println("#ECs: " + verifier.predSize());
        System.out.println("$total: " + (System.nanoTime() - Config.startAt));
    }

    static public Graph<Device, PGEdge> buildGraph(Network network) {
        Graph<Device, PGEdge> graph = new DefaultDirectedGraph<>(PGEdge.class);
        for (Device device : network.getAllDevices()) {
            graph.addVertex(device);
        }
        for (Device device : network.getAllDevices()) {
            for (Port port : device.getPorts()) {
                if (port.getPeerDevice() == null) {
                    continue;
                }
                graph.addEdge(device, port.getPeerDevice(), new PGEdge(device.getPort(device.getName() + ">" + port.getPeerDevice().getName())));
            }
        }
        return graph;
    }
    
    private static void test() {
        Graph<Integer, DefaultEdge> g = new DefaultDirectedGraph<>(DefaultEdge.class);
        g.addVertex(1);
        g.addVertex(2);
        g.addEdge(1, 2);
        System.out.println(g.edgeSet().size());
        Graph<Integer, DefaultEdge> g1 = (Graph<Integer, DefaultEdge>) ((AbstractBaseGraph)(g)).clone();
        g.removeEdge(1, 2);
        System.out.println(g.edgeSet().size());
        System.out.println(g1.edgeSet().size());
    }
}
